/*
*
*	The Gimme ECMAScript Library by Stephen Stchur
*	"Taking the pain out of Javascript!"
*
*	Copyright (c) Microsoft Corporation.  All rights reserved.
*
*	Description:
*	Gimme Effects Extension:	Various "glitzy" effect (powered mostly by the Gimme Animation Module) that can be performed on Gimme objects.
*
*	Requires:
*	gimme.query.js
*	gimme.object.js
*
*/

Gimme.ext.fadeIn = function(_duration, _guid, _callback)
{
	g(this.entities).fadeTo(null, 0.99999, _duration, _guid, _callback);
	_callback = null;
	return this;
};

Gimme.ext.fadeOut = function(_duration, _guid, _callback)
{
	g(this.entities).fadeTo(null, 0, _duration, _guid, _callback);
	_callback = null;
	return this;
};

Gimme.ext.fadeTo = function(_startOpacity, _endOpacity, _duration, _guid, _callback, _accLine)
{
	_accLine = _accLine || Gimme.Animation.AccelerationLines.linear;
	g(this.entities).animate(_accLine, _duration, _guid, _callback, doFade, setup);
	
	function setup(_elem)
	{
		var startOpacity = _startOpacity === 0 ? 0 : _startOpacity || Number(g(_elem).getStyle('opacity'));
		var deltaO = _endOpacity - startOpacity;
		
		return {
		startOpacity: startOpacity,
		deltaO: deltaO };
	}
	
	function doFade(_elem, _pctComplete, _accLine, _args)
	{
		var p = _accLine.getValue(_pctComplete);
		var opacity = _args.startOpacity + p * _args.deltaO;
		g(_elem).setStyle('opacity', opacity);
	}
	
	return this;
};

Gimme.ext.veil = function(_direction, _duration, _guid, _callback, _accLine)
{
	var D = Gimme.Animation.Directions;
	_direction = Math.floor(_direction) || D[_direction] || D.vertically;
	
	g(this.entities).animate(_accLine, _duration, _guid, _callback, doVeil, setup);

	function setup(_elem)
	{
		var height = parseInt(g(_elem).getStyle('height'), 10);
		var width = parseInt(g(_elem).getStyle('width'), 10);
		
		_elem.style.overflow = 'hidden';		
		if ((_direction & D.horizontally) === D.horizontally)
		{
			_elem.style.height = height + 'px';
		}
		
		return {
		deltaH: height,
		deltaW: width };
	}
	
	function doVeil(_elem, _pctComplete, _accLine, _args)
	{
		var p, height, width;
		
		if (_pctComplete >= 1)
		{
			_elem.style.display = 'none';
			_elem.style.height = _elem.style.width = 'auto';
		}
		else
		{
			p = _accLine.getValue(_pctComplete);
			
			if ((_direction & D.vertically) === D.vertically)
			{
				height = _args.deltaH - (p * _args.deltaH);
				_elem.style.height = height + 'px';
			}
			if ((_direction & D.horizontally) === D.horizontally)
			{
				width = _args.deltaW - (p * _args.deltaW);
				_elem.style.width = width + 'px';
			}
		}
	}

	return this;
};

Gimme.ext.unveil = function(_direction, _duration, _guid, _callback, _accLine)
{
	var D = Gimme.Animation.Directions;
	_direction = Math.floor(_direction) || D[_direction] || D.vertically;
	
	g(this.entities).animate(_accLine, _duration, _guid, _callback, doUnveil, setup);

	function setup(_elem)
	{
		var styleW = _elem.style.width;
		var styleH = _elem.style.height;
		var styleOverflow = _elem.style.overflow;
		
		var clone = _elem.cloneNode(true);
		clone.style.visibility = 'hidden';
		clone.style.margin = '0';
		clone.style.padding = '0';
		clone.style.border = '0';
		clone.style.position = 'absolute';
		clone.style.display = 'block';
		_elem.parentNode.appendChild(clone);
		var height = clone.offsetHeight;
		var width = clone.offsetWidth;
		clone.parentNode.removeChild(clone);
		
		_elem.style.visibility = 'visible';
		_elem.style.display = 'block';
		_elem.style.overflow = 'hidden';
		
		if ((_direction & D.horizontally) === D.horizontally)
		{
			_elem.style.height = height + 'px';
		}
		
		return {
		deltaH: height,
		deltaW: width,
		styleH: styleH,
		styleW: styleW,
		styleOverflow: styleOverflow };
	}
	
	function doUnveil(_elem, _pctComplete, _accLine, _args)
	{
		var p, height, width;
		
		if (_pctComplete >= 1)
		{
			_elem.style.overflow = _args.styleOverflow;
			_elem.style.width = _args.styleW;
			_elem.style.height = _args.styleH;
		}
		else
		{
			p = _accLine.getValue(_pctComplete);
			
			if ((_direction & D.vertically) === D.vertically)
			{
				height = p * _args.deltaH;
				_elem.style.height = height + 'px';
			}
			if ((_direction & D.horizontally) === D.horizontally)
			{
				width = p * _args.deltaW;
				_elem.style.width = width + 'px';
			}
		}
	}
	
	return this;
};

Gimme.ext.scrollTo = function (_duration, _guid, _callback, _accLine)
{
	g(this.entities[0]).animate(_accLine, _duration, _guid, _callback, doScroll, setup);

	function setup(_elem)
	{	
		var scrollPos = Gimme.Screen.getScrollPosition();
		var deltaY = g(_elem).getPagePosition().y - scrollPos.y;
		
		return {
		scrollPos: scrollPos,
		deltaY: deltaY };
	}
	
	function doScroll(_elem, _pctComplete, _accLine, _args)
	{
		var p = _accLine.getValue(_pctComplete);
		
		var y = _args.scrollPos.y + p * _args.deltaY;
		window.scrollTo(0, Math.floor(y));	
	}
	
	return this;
};

Gimme.ext.slideToPoint = function(_endPt, _duration, _guid, _callback, _accLine)
{
	g(this.entities).animate(_accLine, _duration, _guid, _callback, doSlide, setup);	

	function setup(_elem)
	{
		var startPt = g(_elem).getComputedPosition();
		
		if (_endPt.x === null) { _endPt.x = startPt.x; }
		if (_endPt.y === null) { _endPt.y = startPt.y; }
		
		var deltaY = _endPt.y - startPt.y;
		var deltaX = _endPt.x - startPt.x;
		
		return {
		startPt: startPt,
		deltaX: deltaX,
		deltaY: deltaY };
	}
	
	function doSlide(_elem, _pctComplete, _accLine, _args)
	{
		var startPt = _args.startPt;
		var deltaX = _args.deltaX;
		var deltaY = _args.deltaY;
	
		var p = _accLine.getValue(_pctComplete);

		var x = startPt.x + p * deltaX;
		var y = startPt.y + p * deltaY;
		
		_elem.style.top = Math.floor(Math.round(y)) + 'px';
		_elem.style.left = Math.floor(Math.round(x)) + 'px';		
	}
	
	return this;
};

Gimme.ext.followPath = function(_path, _factor, _duration, _guid, _callback)
{
	_factor = _factor || 1;
	g(this.entities).animate(_path, _duration, _guid, _callback, doFollowPath, setup);
	
	function setup(_elem)
	{
		var startPt = g(_elem).getComputedPosition();
		
		return {
		startPt: startPt };
	}
	
	function doFollowPath(_elem, _pctComplete, _path, _args)
	{
		var startPt = _args.startPt;
		
		var p = _path.getPoint(_pctComplete);
		
		var x = startPt.x + p.x * _factor;
		var y = startPt.y + p.y * _factor;
			
		_elem.style.top = Math.floor(Math.round(y)) + 'px';
		_elem.style.left = Math.floor(Math.round(x)) + 'px';
	}
	
	return this;
};

Gimme.ext.animate = function(_path, _duration, _guid, _callback, _logicFn, _setupFn)
{
	// abort the animation request if there are no DOM elements to animate
	var numElems = this.entities.length;
	if (numElems < 1)
	{
		return;
	}	
	
	// use the defaultCurve is none is specified
	_path = _path || Gimme.Animation.AccelerationLines.defaultLine;
	
	// determine the duration of the animation (either user specified via keyword or number, or default to 'quickly')
	_duration = Math.floor(_duration) || Gimme.Animation.Speeds[_duration] || Gimme.Animation.Speeds.quickly;
	
	// auto generate a GUID for this animation if one wasn't specified
	_guid = _guid || 'AUTOGUID_' + Math.random(new Date().getTime());
	
	// if an animation marked by the given GUID is already running (exists in the hash) don't try to start it again
	if (!Gimme.Animation.isGrouping() && Gimme.Animation.isRunning(_guid))
	{
		return;
	}
	
	Gimme.Animation.whenDone(_guid, _callback);
	
	var numFinished = 0;
	var numPoints = _path.points.length;
	var startTime = new Date().getTime();
	
	this.forEach(function(_elem, _index)
	{
		Gimme.Animation.start(_guid, doAnimate(_elem, _index), 10);
	});
	
	function doAnimate(_el, _i)
	{
		var args = _setupFn(_el);
		
		return function()
		{
			var t = new Date().getTime();
			var pct = (t - startTime) / _duration;
			
			if (pct >= 1)
			{
				if (typeof _logicFn === 'function')
				{
					_logicFn(_el, 1, _path, args);
				}
				
				if (++numFinished === numElems)
				{
					Gimme.Animation.end(_guid);
					_setupFn = null;
					_logicFn = null;
				}
			}
			else
			{
				_logicFn(_el, pct, _path, args);			
			}
		};		
	}	
};

